#ifndef _KC_SAFE_QUEUE__H_KC_
#define _KC_SAFE_QUEUE__H_KC_

#include <iostream>
#include <queue>
#include <stdexcept>
#include <boost/noncopyable.hpp>
#include <boost/thread/mutex.hpp>
#include <boost/thread/condition_variable.hpp>

namespace kc_sf
{

	// Thread-safe queue
	template <typename Elem >
	class SafeQueue : boost::noncopyable
	{
		public:

			SafeQueue() {}
			~SafeQueue() {}

			void push( const Elem &elem );
			bool tryPop( Elem &elem );
			Elem waitAndPop();

			bool empty() const;
			size_t size() const;

		private:

			boost::mutex	sq_mutex;
			std::queue<Elem >	sq_queue;
			boost::condition_variable sq_condition_variable;
	};

	// ------------------------------------------------------------------ //

	template <typename Elem >
	void SafeQueue<Elem >::push( const Elem &elem )
	{
		boost::mutex::scoped_lock lock( sq_mutex );
		sq_queue.push(elem);

        lock.unlock();
        sq_condition_variable.notify_one();
	}

	// ------------------------------------------------------------------ //

	template <typename Elem >
	bool SafeQueue<Elem >::tryPop( Elem &popped_elem )
	{
        boost::mutex::scoped_lock lock( sq_mutex );
        if( sq_queue.empty() )
            return false;

        popped_elem = sq_queue.front();
        sq_queue.pop();
        return true;
	}

	// ------------------------------------------------------------------ //

	template <typename Elem >
	Elem SafeQueue<Elem >::waitAndPop()
	{
		boost::mutex::scoped_lock lock( sq_mutex );

        while( sq_queue.empty() )
            sq_condition_variable.wait( lock );

        Elem popped_elem = sq_queue.front();
        sq_queue.pop();

        return popped_elem;
	}

	// ------------------------------------------------------------------ //

	template <typename Elem >
	bool SafeQueue<Elem >::empty() const
	{
		boost::mutex::scoped_lock lock( sq_mutex );
		return sq_queue.empty();
	}

	// ------------------------------------------------------------------ //

	template <typename Elem >
	size_t SafeQueue<Elem >::size() const
	{
		boost::mutex::scoped_lock lock( sq_mutex );
		return sq_queue.size();
	}
}

#endif // _KC_SAFE_QUEUE__H_KC_
